﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;

namespace XUHttpDownload
{
    public abstract class XUHttpSocketBase
    {
        protected string server { get; private set; }
        protected int port { get; private set; }

        public XUHttpSocketBase(string server, int port)
        {
            this.server = server;
            this.port = port;
        }

        protected Socket ConnectSocket(string server, int port)
        {
            IPHostEntry hostEntry = Dns.GetHostEntry(server);
            foreach (IPAddress address in hostEntry.AddressList)
            {
                try
                {
                    IPEndPoint ipe = new IPEndPoint(address, port);
                    Socket tempSocket = new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                    tempSocket.Connect(ipe);
                    if (tempSocket.Connected)
                    {
                        return tempSocket;
                    }
                    else
                    {
                        continue;
                    }
                }
                catch (Exception ex)
                {
                    Debug.LogError("XUHttpSocketBase exception " + ex);
                    continue;
                }
            }
            return null;
        }

        protected void DisconnectSocket(Socket socket)
        {
            if (socket != null)
            {
                if (socket.Connected)
                {
                    try
                    {
                        socket.Shutdown(SocketShutdown.Both);
                        socket.Disconnect(true);
                    }
                    catch
                    {
                        socket.Close();
                    }
                    finally
                    {
                        socket.Close();
                    }
                }
                else
                {
                    socket.Close();
                }
            }
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        protected bool SocketSend(Socket socket, string request)
        {
            try
            {
                byte[] bytesSent = Encoding.ASCII.GetBytes(request);
                int sendSize = socket.Send(bytesSent, bytesSent.Length, 0);
                if (sendSize == bytesSent.Length)
                {
                    return true;
                }
                else
                {
                    Debug.LogError("XUHttpSocketBase error, send bytes : " + sendSize + "/" + bytesSent.Length);
                    return false;
                }
            }
            catch (Exception ex)
            {
                Debug.LogError("XUHttpSocketBase exception " + ex);
                return false;
            }
        }

        /// <summary>
        /// 接收文件数据
        /// </summary>
        /// <returns>返回长度</returns>
        protected int SocketReceive(Socket socket, XUHttpSocketRequest request)
        {
            int rsize = socket.Receive(request.tempBuffer, request.tempBuffer.Length, SocketFlags.None);
            for (int i = 0; i < rsize; i++)
            {
                request.fileBuffer[request.fileOffset++] = request.tempBuffer[i];
            }
            //获取文件大小
            if (request.fileLength == 0)
            {
                string headContent = ParseHeader(request.fileBuffer); //头部文本
                if (string.IsNullOrEmpty(headContent) == false)
                {
                    request.responseHeader = headContent;

                    string[] splits = headContent.Split('\n');
                    foreach (var split in splits)
                    {
                        if (split.Contains("Content-Length: "))
                        {
                            //获取文件长度
                            request.fileLength = int.Parse(split.Replace("Content-Length: ", ""));
                            break;
                        }
                    }

                    //真实的文件字节数要减去头部的真实长度
                    for (int i = headContent.Length; i < request.fileOffset; i++)
                    {
                        request.fileBuffer[i - headContent.Length] = request.fileBuffer[i];
                    }
                    request.fileOffset -= headContent.Length;
                }
            }
            //文件下载完成
            if (request.fileLength > 0 && request.fileLength == request.fileOffset)
            {
                request.status = XUHttpSocketRequest.Status.Response;
            }
            return rsize;
        }

        private string ParseHeader(byte[] headerBytes)
        {
            string headerStr = string.Empty;
            int index = 0;
            char tmp;
            int flag = 0; //用来标志是否出现了\r\n\r\n，即header结尾
            bool end = false;//用来标志是否出现了\r\n\r\n，即header结尾
            do
            {
                tmp = Convert.ToChar(headerBytes[index++]);//读下一个字符
                switch (tmp)
                {
                    case '\r':
                        break;
                    case '\n'://读到一行结尾，不管
                        flag++;
                        if (flag == 2)
                            end = true;
                        break;
                    default:
                        flag = 0; //出现字符，标志清0
                        break;
                }
                headerStr += tmp;
                if (end)   //到结尾了，直接不读了
                    break;
            } while (index < headerBytes.Length);

            if (end == false)
            {
                headerStr = string.Empty;
            }
            return headerStr;
        }

        protected string GetRequest(string url)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(@"GET " + url + " HTTP/1.1");
            sb.Append("\r\n");
            sb.Append(@"Host: " + url.Split('/')[2]);
            sb.Append("\r\n");
            sb.Append("\r\n");//这个一定要有不然接收回来可能没有数据
            return sb.ToString();
        }

        protected long GetMilliseconds()
        {
            return Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalMilliseconds);
        }
    }
}